#include "Material.h"
#include <qgl.h>
#include "GlTextureManager.h"
#include "PersistenceManager.h"


Material::Material() : _ambientColor (0.2, 0.2, 0.2, 1.0),
		_diffuseColor (0.8, 0.8, 0.8, 1.0),
		_specularColor (0.0, 0.0, 0.0, 1.0), _shininess (50)
{
}

Material::~Material()
{

}

void Material::setAmbientColor (const glm::vec3 & color)
{
	_ambientColor = glm::vec4 (color, 1.0);
}

glm::vec3 Material::getAmbientColor () const
{
	return _ambientColor.swizzle (glm::X, glm::Y, glm::Z);
}

void Material::setDiffuseColor (const glm::vec3 & color)
{
	_diffuseColor = glm::vec4 (color, 1.0);
}

glm::vec3 Material::getDiffuseColor () const
{
	return _diffuseColor.swizzle (glm::X, glm::Y, glm::Z);
}

void Material::setSpecularColor (const glm::vec3 & color)
{
	_specularColor = glm::vec4 (color, 1.0);
}

glm::vec3 Material::getSpecularColor () const
{
	return _specularColor.swizzle (glm::X, glm::Y, glm::Z);
}

void Material::setShininess (int shininess)
{
	_shininess = shininess;
}

int Material::getShininess () const
{
	return _shininess;
}

void Material::configMaterialAt (QGLWidget * pDev)
{
	glDisable (GL_COLOR_MATERIAL);
	glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, &_ambientColor[0]);
	glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, &_diffuseColor[0]);
	glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, &_specularColor[0]);
	glMateriali (GL_FRONT_AND_BACK, GL_SHININESS, _shininess);

	RenderInfo * pRenderInfo;
	if (pDev != NULL) {
		registerDev (pDev);
		pRenderInfo = findRenderInfoByDev (pDev);
		pRenderInfo->liveness = 10;
	}

	for (int i = 0; i < _renderInfos.size (); ++i) {
		--_renderInfos[i].liveness;
		if (_renderInfos[i].liveness <= 0)
			removeDev (_renderInfos[i].pDev);
	}

	if (pDev == NULL || _texImageName == "") {
		// No texture
		glDisable (GL_TEXTURE_2D);
		return;
	}

	if (pRenderInfo->isNeedUpdate)
		_rebindTexture (pRenderInfo);
	if (pRenderInfo->texId == -1)
		throw ("Fail to bind texture");

	glEnable (GL_TEXTURE_2D);
	glBindTexture (GL_TEXTURE_2D, pRenderInfo->texId);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}


void Material::setName (const QString & name)
{
	_name = name;
}

QString Material::getName () const
{
	return _name;
}

void Material::setTexImageName (const QString & texImageName)
{
	if (texImageName == _texImageName)
		return;
	_texImageName = texImageName;
	for (int i = 0; i < _renderInfos.size (); ++i)
		_renderInfos[i].isNeedUpdate = true;
}

QString Material::getTexImageName () const
{
	return _texImageName;
}

void Material::registerDev (QGLWidget * pDev)
{
	if (findRenderInfoByDev (pDev) != NULL)
		return;
	RenderInfo newRenderInfo = { pDev, -1, true, 10 };
	_renderInfos.push_back (newRenderInfo);
}

void Material::removeDev (QGLWidget * pDev)
{
	for (std::vector< RenderInfo >::iterator iter = _renderInfos.begin ();
			iter != _renderInfos.end (); ++iter) {
		if (iter->pDev == pDev) {
			_renderInfos.erase (iter);
			return;
		}
	}
}

Material::RenderInfo * Material::findRenderInfoByDev (QGLWidget * pDev)
{
	for (std::vector< RenderInfo >::iterator iter = _renderInfos.begin ();
			iter != _renderInfos.end (); ++iter) {
		if (iter->pDev == pDev)
			return &(*iter);
	}
	return NULL;
}

void Material::_rebindTexture (RenderInfo * pRenderInfo)
{
	pRenderInfo->texId = GlTextureManager::getSingleton ().
			getTextureFromFile (_texImageName);
	pRenderInfo->isNeedUpdate = false;
}

QDataStream & operator>> (QDataStream & inStream, Material & material)
{
	inStream >> material._name;
	inStream >> material._texImageName;
	inStream >> material._ambientColor[0] >> material._ambientColor[1] >>
			material._ambientColor[2] >> material._ambientColor[3];
	inStream >> material._diffuseColor[0] >> material._diffuseColor[1] >>
			material._diffuseColor[2] >> material._diffuseColor[3];
	inStream >> material._specularColor[0] >> material._specularColor[1] >>
			material._specularColor[2] >> material._specularColor[3];
	inStream >> material._shininess;

	if (material._texImageName == "")
		return inStream;

	// Try load texture
	try {
		int tempTexId = GlTextureManager::getSingleton ().
				getTextureFromFile (material._texImageName);
		GlTextureManager::getSingleton ().deleteTexture (tempTexId);
	} catch (char * errString) {
		PersistenceManager::getSingleton ().addError (
				QString ("Fail to load texture ") + 
				material._texImageName + " while loading material " + 
				material._name + ": " + errString);
		material._texImageName = "";
	}
	return inStream;
}

QDataStream & operator<< (QDataStream & outStream, Material & material)
{
	outStream << material._name;
	outStream << material._texImageName;
	outStream << material._ambientColor[0] << material._ambientColor[1] <<
			material._ambientColor[2] << material._ambientColor[3];
	outStream << material._diffuseColor[0] << material._diffuseColor[1] <<
			material._diffuseColor[2] << material._diffuseColor[3];
	outStream << material._specularColor[0] << material._specularColor[1] <<
			material._specularColor[2] << material._specularColor[3];
	outStream << material._shininess;
	return outStream;
}
